﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using CI.Interfaces;

namespace CI.Organism
{
    /// <summary>Cells collection</summary>
    [CollectionDataContract(IsReference=true)]
    public class CellCollection : List<ICell>, ICellCollection
    {
        /// <summary>
        /// Take this parent cell collection and create a new cell collection to be put in the new child Organism
        /// Mutate the current cells and return a new one, deep class level mutation
        /// </summary>
        /// <param name="mutationRate">mutation rate to be applied</param>
        /// <param name="childOrganism">child Organism it will be going into</param>
        /// <returns>new cell collection for child</returns>
        public ICellCollection Mutate(double mutationRate, IOrganism childOrganism)
        {
            #region Argument check
            if (!(childOrganism is IOrganism))
                throw new ArgumentException("Organism");
            #endregion

            // create the new chromosome collection
            var childCells = new CellCollection();

            // set this childOrganism to use these child cells
            childOrganism.Cells = childCells;

            // currently just mutate the existing ones
            foreach (var cell in this)
            {
                // the cell mutate function adds to the childOrganism cell collection in the cell base class
                cell.Mutate(mutationRate, childOrganism);
            }

            // mutate making new ones
            // we have a method in cellular gene that handles makeing new cells!!!!!

            // mutate removing some cells
            // todo  : possible bug may arise if we take out a cell that a gene tries to work with???
            if (Mutation.MutateState(mutationRate) && (childCells.Count > 0))
            {
                childCells.RemoveRange(Mutation.CollectionSelect(childCells), 1);
            }

            return childCells;
        }

        /// <summary>
        /// Find the cell with the specifed guid
        /// </summary>
        /// <param name="guid">guid to find</param>
        /// <returns>Cell</returns>
        public ICell Find(Guid guid)
        {
            return FindLast(cell => cell.Guid == guid);
        }

        /// <summary>
        /// Find the cell with the specifed guid as a string
        /// </summary>
        /// <param name="guid">guid to find as a string</param>
        /// <returns>Cell</returns>
        public ICell Find(string guid)
        {
            return FindLast(cell => cell.Guid.ToString() == guid);
        }

        /// <summary>
        /// Find the base physical cell that everything is build off
        /// </summary>
        public ICell FindPhysicalZygote()
        {
            // if their are no cells in this collection then we wont find it
            if (Count == 0)
                return null;

            if (_foundZygote != this[0])
                _foundZygote = Find(cellType => cellType is IPhysical);

            return _foundZygote;
        }
        private ICell _foundZygote;

        /// <summary>
        /// this will scan all the cells and find neurons
        /// </summary>
        /// <returns></returns>
        public INeuronCollection Neurons()
        {
            // collection to return
            INeuronCollection neurons = new NeuronCollection();

            foreach (var neuron in this.OfType<INeuron>())
            {
                neurons.Add(neuron);
            }

            return neurons;
        }

        /// <summary>
        /// do a equals comparison of one cell collection type to another only comparing the value types, no refference comparisons
        /// </summary>
        /// <param name="cells">cells to compare against</param>
        /// <returns>true if equal value types</returns>
        public bool EqualsValueTypes(ICellCollection cells)
        {
            // make sure we have something to compare it against
            if (cells == null)
                return false;

            // do a count check first
            if (Count != cells.Count)
                return false;

            // compare each cell in the organism against the child one
            foreach (var cell in this)
            {
                // find the cell in the child organism
                var foundCell = cells.Find(cell.Guid);

                // see if we got one
                if (foundCell == null)
                    return false;

                // check types to make sure their the same
                if (!(cell.GetType().Equals(foundCell.GetType())))
                    return false;

                // call the cell check
                if (!cell.EqualsValueTypes(foundCell))
                    return false;
            }

            return true;
        }

    }
}